package com.miniapp.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.miniapp.context.UserContext;
import com.miniapp.entity.BaseFile;
import com.miniapp.mapper.BaseFileMapper;
import com.miniapp.service.BaseFileService;
import com.miniapp.service.FileStorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

/**
 * 基础文件服务实现类
 *
 * <p>实现BaseFileService接口，处理文件的上传、下载、删除及临时文件管理
 * <p>支持临时文件转正式文件功能
 *
 * @author yourName
 * @date 2025-09-06
 */
@Service
public class BaseFileServiceImpl extends ServiceImpl<BaseFileMapper, BaseFile> implements BaseFileService {

    private static final Logger log = LoggerFactory.getLogger(BaseFileServiceImpl.class);

    @Autowired
    private FileStorageService storageService;

    /**
     * 日期格式化器，格式为yyyyMMdd
     */
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 临时文件根目录
     */
    private static final String TEMPORARY_DIR_PREFIX = "temporary/";

    /**
     * 正式文件根目录
     */
    private static final String FORMAL_DIR_PREFIX = "formal/";

    /**
     * 临时文件默认有效期（24小时）
     */
    private static final int TEMPORARY_FILE_EXPIRE_HOURS = 24;

    /**
     * 上传文件（支持临时/正式文件标记）
     *
     * @param file 上传的文件
     * @param isTemporary 是否为临时文件
     * @param dir 存储目录（为空时自动生成）
     * @return 基础文件实体
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseFile uploadFile(MultipartFile file, boolean isTemporary, String dir) {
        // 获取当前登录用户ID
        Long uploaderId = UserContext.getUserId();

        // 处理存储目录：根据文件类型添加根目录前缀
        String storageDir = buildStorageDir(dir, isTemporary);

        // 上传文件到存储服务
        Map<String, String> storageResult = storageService.upload(file, storageDir);

        // 构建BaseFile实体
        BaseFile baseFile = new BaseFile();
        baseFile.setFileKey(storageResult.get("fileKey"));
        baseFile.setFileUrl(storageResult.get("fileUrl"));
        baseFile.setFileName(storageResult.get("originalFileName"));
        baseFile.setStorageFileName(storageResult.get("fileName"));
        baseFile.setFileType(storageResult.get("contentType"));
        baseFile.setFileSize(Long.valueOf(storageResult.get("fileSize")));
        baseFile.setUploaderId(uploaderId);
        baseFile.setIsTemporary(isTemporary ? 1 : 0);
        baseFile.setCreateTime(LocalDateTime.now());
        baseFile.setUpdateTime(LocalDateTime.now());

        // 设置临时文件过期时间
        if (isTemporary) {
            baseFile.setExpireTime(LocalDateTime.now().plusHours(TEMPORARY_FILE_EXPIRE_HOURS));
        }

        // 保存到数据库
        baseMapper.insert(baseFile);

        return baseFile;
    }

    /**
     * 将临时文件转换为正式文件
     *
     * @param fileId 临时文件ID
     * @param businessType 业务类型
     * @param businessCode 业务编码
     * @return 转换后的正式文件
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseFile convertToFormalFile(Long fileId, String businessType, String businessCode) {
        // 1. 查询文件并验证状态
        BaseFile tempFile = baseMapper.selectById(fileId);
        if (tempFile == null) {
            throw new IllegalArgumentException("文件不存在，fileId: " + fileId);
        }
        if (tempFile.getIsTemporary() != 1) {
            throw new IllegalArgumentException("只能转换临时文件，fileId: " + fileId);
        }
        if (tempFile.getExpireTime() != null && LocalDateTime.now().isAfter(tempFile.getExpireTime())) {
            throw new IllegalArgumentException("临时文件已过期，fileId: " + fileId);
        }

        // 2. 构建正式文件存储路径
        String formalDir = buildFormalDirectory(businessType, businessCode);
        String newFileKey = formalDir + "/" + tempFile.getStorageFileName();
        String oldFileKey = tempFile.getFileKey();

        // 3. 使用服务器端复制文件
        boolean copySuccess = storageService.copy(oldFileKey, newFileKey);

        if (!copySuccess) {
            throw new RuntimeException("文件复制失败，fileId: " + fileId);
        }

        try {
            // 4. 更新文件信息为正式文件
            tempFile.setFileKey(newFileKey);
            tempFile.setFileUrl(storageService.getFileUrl(newFileKey));
            tempFile.setIsTemporary(0);
            tempFile.setExpireTime(null);
            tempFile.setUpdateTime(LocalDateTime.now());
            baseMapper.updateById(tempFile);

            // 5. 删除原临时文件
            storageService.delete(oldFileKey);

            return tempFile;
        } catch (Exception e) {
            log.error("文件信息更新失败，fileId: {}", fileId, e);
            // 如果更新失败，尝试删除已复制的新文件
            try {
                storageService.delete(newFileKey);
            } catch (Exception ex) {
                log.warn("无法回滚已复制的新文件: {}", newFileKey, ex);
            }
            throw new RuntimeException("文件信息更新失败：" + e.getMessage(), e);
        }
    }

    /**
     * 根据文件ID获取文件输入流
     *
     * @param fileId 文件ID
     * @return 文件输入流
     */
    @Override
    public InputStream getFileInputStream(Long fileId) {
        BaseFile baseFile = baseMapper.selectById(fileId);
        if (baseFile == null) {
            throw new IllegalArgumentException("文件不存在，fileId: " + fileId);
        }
        return storageService.download(baseFile.getFileKey());
    }

    /**
     * 删除文件（同时删除存储和数据库记录）
     *
     * @param fileId 文件ID
     * @return 是否删除成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteFile(Long fileId) {
        BaseFile baseFile = baseMapper.selectById(fileId);
        if (baseFile == null) {
            log.warn("删除文件失败，文件不存在，fileId: {}", fileId);
            return false;
        }

        // 1. 删除存储服务中的文件
        boolean storageDeleted = storageService.delete(baseFile.getFileKey());

        // 2. 删除数据库记录
        if (storageDeleted) {
            baseMapper.deleteById(fileId);
            log.info("文件删除成功，fileId: {}", fileId);
            return true;
        }

        log.error("文件删除失败，存储服务删除失败，fileId: {}", fileId);
        return false;
    }

    /**
     * 清理过期的临时文件
     *
     * @return 清理的文件数量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cleanExpiredTemporaryFiles() {
        // 1. 查询所有已过期的临时文件
        LambdaQueryWrapper<BaseFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BaseFile::getIsTemporary, 1)
                .lt(BaseFile::getExpireTime, LocalDateTime.now());

        List<BaseFile> expiredFiles = baseMapper.selectList(queryWrapper);
        if (expiredFiles.isEmpty()) {
            return 0;
        }

        // 2. 逐个删除
        int deleteCount = 0;
        for (BaseFile file : expiredFiles) {
            try {
                // 删除存储中的文件
                storageService.delete(file.getFileKey());
                // 删除数据库记录
                baseMapper.deleteById(file.getId());
                deleteCount++;
            } catch (Exception e) {
                // 记录日志，继续处理下一个文件
                log.error("清理过期文件失败，文件ID: {}", file.getId(), e);
            }
        }

        log.info("清理过期临时文件完成，共清理: {} 个，失败: {} 个",
                deleteCount, expiredFiles.size() - deleteCount);
        return deleteCount;
    }

    /**
     * 构建存储目录（添加类型前缀和年月日目录）
     *
     * @param dir 原始目录
     * @param isTemporary 是否为临时文件
     * @return 处理后的存储目录，格式为：[类型前缀]/[原始目录]/[yyyyMMdd] 或 [类型前缀]/[yyyyMMdd]
     */
    private String buildStorageDir(String dir, boolean isTemporary) {
        // 添加文件类型前缀
        String prefix = isTemporary ? TEMPORARY_DIR_PREFIX : FORMAL_DIR_PREFIX;

        // 获取当前日期的yyyyMMdd格式
        String dateDir = LocalDate.now().format(DATE_FORMATTER);

        // 处理目录拼接
        StringBuilder storageDir = new StringBuilder(prefix);

        // 如果原始目录不为空，则添加原始目录
        if (dir != null && !dir.trim().isEmpty()) {
            // 处理原始目录开头的斜杠，避免重复
            String processedDir = dir.startsWith("/") ? dir.substring(1) : dir;
            storageDir.append(processedDir).append("/");
        }
        // 添加年月日目录
        storageDir.append(dateDir);
        return storageDir.toString();
    }

    /**
     * 构建正式文件目录
     */
    private String buildFormalDirectory(String businessType, String businessCode) {
        return String.format("%s%s/%s/%s",
                FORMAL_DIR_PREFIX,
                businessType,
                businessCode,
                LocalDate.now().format(DATE_FORMATTER));
    }
}
